package de.ostfalia.robotik.rob3;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;


public class Comm implements SerialPortEventListener {
	private SerialPort mSerialPort;
	private OutputStream mOutStream;
	private InputStream mInStream;
	private BufferedReader mInBufferedReader;
	private int[] mWaitForChars;
	private int mWaitForCharFound;			
	private String mReceiveMessageBuffer;

	public Comm(String portName) throws NoSuchPortException, PortInUseException {
		portName = portName.replace("COM", "");
		portName = portName.replace(":", "");
		int portNr = Integer.parseInt(portName);
		
		CommPortIdentifier cpi = CommPortIdentifier.getPortIdentifier("COM" + portNr);
		mSerialPort = (SerialPort)cpi.open("ROB3", 1000);
		try {
			mSerialPort.setSerialPortParams(2400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
			mOutStream = mSerialPort.getOutputStream();
	  	mInStream = mSerialPort.getInputStream();
			mSerialPort.addEventListener(this); 
			mSerialPort.notifyOnDataAvailable(true);
			InputStreamReader isr = new InputStreamReader(mInStream);
			mInBufferedReader = new BufferedReader(isr);			
		} catch (UnsupportedCommOperationException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TooManyListenersException e) {
			e.printStackTrace();
		}
		mReceiveMessageBuffer = "";
	}

	public void send(int byt) {
		try {
			mReceiveMessageBuffer = "";
			mOutStream.write(byt);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void send(byte[] data) {
		try {
			mReceiveMessageBuffer = "";
			mOutStream.write(data);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public int waitForChar(int byt, int timeout) {
		int[] bytes = new int[1];
		bytes [0] = byt;
		return waitForChar(bytes, timeout);
	}
	
	public int waitForChar(int[] bytes, int timeout) {
		int time = 0;
		mWaitForChars = bytes;
		mWaitForCharFound = -1;
		do {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			time += 100;
		} while (mWaitForCharFound < 0 && time < timeout);
		if (mWaitForCharFound < 0) {
			System.out.println("[Comm] waitForChar: Timeout while waiting for char");
		}
		return mWaitForCharFound;
	}

	public byte[] receiveMessage(int terminator, int timeout) {
		mReceiveMessageBuffer = "";
		if (waitForChar(terminator, timeout) > -1) {
			return mReceiveMessageBuffer.getBytes();
		} else {
			return null;
		}
	}

	public void serialEvent(SerialPortEvent event) {
		switch(event.getEventType()) {
		case SerialPortEvent.DATA_AVAILABLE:
			try {
				char[] buffer = new char[100];
				while (mInBufferedReader.ready()) {
					int cnt = mInBufferedReader.read(buffer);
					for (int i = 0; i < cnt; i++) {
						mReceiveMessageBuffer += buffer[i];
						//System.out.println("Recv: 0x" + Integer.toHexString(buffer[i]));
						for (int j = 0; j < mWaitForChars.length; j++) {
  						if (mWaitForChars[j] == buffer[i]) {
  							mWaitForCharFound = j;
  							break;
  						}
						}
					}
				};
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void getAvailablePorts() {
		List<String> ports = new ArrayList<String>();
    CommPortIdentifier serialPortId;
    Enumeration<CommPortIdentifier> enumComm;

    enumComm = CommPortIdentifier.getPortIdentifiers();
    while (enumComm.hasMoreElements()) {
     	serialPortId = enumComm.nextElement();
     	if(serialPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
    		ports.add(serialPortId.getName());
    	}
    }		
	}
}
